var _ = require('lodash');
import XLog from './XLog';
import { short2Hex, arrayPushData, array2Hex, MeterQueryType, Recv_Error_Def, number2Hex, CRC, hex2Int } from "./SerialCommon";

const meterConstant = {
    functionCode: "03",

    response_pos_length: 4,
    response_pos_data: 6,

    response_non_data_length: 10,

    MeterResendCount: 3,
    MeterStatCount: 10,
}

function generateMeterRequest(address, request){
    var result = new Array();
    result.push(address);                               // 插入设备地址
    result.push(meterConstant.functionCode);            // 插入功能码
    result = arrayPushData(result, short2Hex(request.address));    // 查询地址
    result = arrayPushData(result, short2Hex(request.length));     // 查询长度    
    result = arrayPushData(result, CRC.ForMeterCRC16(result));   // CRC字段
    return array2Hex(result);
}

function checkResponseLength(request, response){
    return hex2Int(response.substr(meterConstant.response_pos_length, 2)) == request.length * 2;
}

function getResponseBuff(request, response){
    if(checkResponseLength(request, response)){
        return response.substr(meterConstant.response_pos_data, hex2Int(response.substr(meterConstant.response_pos_length, 2)) * 2);
    }
    return "";
    // return response.substr(meterConstant.response_pos_data, hex2Int(response.substr(meterConstant.response_pos_length, 2)) * 2);
}

function fetchResponseAddress(response){
    return response.substr(0, 2);
}

    // 5 个长度以外的数据，包括地址(1) + 起始码(1) + 功能码(1) 校验和(2)
function checkRecvLen(address, msg){
    const lenFormat = 5;
    if(msg.substr(0, 2) != address){
        console.log("meter checkRecvLen address: " + address + " recv: [" + msg.substr(0, 2) + "]");
        return Recv_Error_Def.ERR_START_FLAG;
    }
    if (msg.length < meterConstant.response_pos_length + 2){
        return Recv_Error_Def.ERR_NOT_ENOUGH_LEN;
    }

    let dataLen = hex2Int(msg.substr(meterConstant.response_pos_length, 2));
    let expectLen = (dataLen + lenFormat) * 2;

    if (msg.length < expectLen){
        return Recv_Error_Def.ERR_NOT_ENOUGH_LEN;
    }
    let checksum_sum = CRC.ForMeterCRC16(msg.substr(0, expectLen - 4));
    let checksum_recv = msg.substr(expectLen - 4, 4);
    console.log("checksum_sum: ", checksum_sum);
    console.log("checksum_recv: ", checksum_recv);
    if(CRC.ForMeterCRC16(msg.substr(0, expectLen - 4)) != msg.substr(expectLen - 4, 4)){
        return Recv_Error_Def.ERR_CHECKSUM;
    }

    return expectLen;
}

export default class SerialMeterClient{
    constructor(address, index, id){
        try{
            this.address = number2Hex(parseInt(address));
        }catch(error){
            XLog.e("init address error: ", error?.message);
        }
        
        this.index = index;
        this.id = id;
        this.function = {};
        this.requestList = [];
        this.realtimeList = [];
        this.statList = [];
        this.statCount = 0;
        this.currentRecvstr = "";

        this.addFunction("sendRequest", this.sendRequest.bind(this));
    }

    sendRequest(data) {
        if(this.address == null){
            XLog.e("smart meter address null");
            return "";
        }
        return generateMeterRequest(this.address, data);
    }    

    addFunction(event, callback){
        this.function[event] = callback;
    }

    setRequestList(request){
        this.requestList = [...request];
        this.realtimeList = [];
        this.statList = [];
        this.statCount++;
        console.log("setRequestList", this.requestList);
    }

    fillResultList(request, buff){
        let result = { address: request.address, length: request.length, type: request.type, buff:  buff }
        if(request.type == MeterQueryType.REALTIME){
            this.realtimeList.push(result);
        } else if(request.type == MeterQueryType.STAT){
            this.statList.push(result);
        }
    }

    getRequestCountByType(type){
        let count = 0;
        if(this.requestList?.length > 0){
            this.requestList.map((item, index) => {
                if(item.type == type){
                    count++;
                }
            })
        }
        return count;
    }

    checkRecvByte(inputMsg){
        this.currentRecvstr = this.currentRecvstr + inputMsg;
        return checkRecvLen(this.address, this.currentRecvstr);
    }

    matchAddress(inputMsg, address){
        if(address == null || address.length == 0){
            return false;
        }
        let response_address = fetchResponseAddress(inputMsg);
        return response_address.toUpperCase() == address.toUpperCase();
    }

    handleReceiveMessage(inputMsg){
        let checkRecvRet = this.checkRecvByte(inputMsg);
        if(checkRecvRet > 0){
            message = this.currentRecvstr.substr(0, checkRecvRet);
            this.currentRecvstr = this.currentRecvstr.length > checkRecvRet ? this.currentRecvstr.substr(checkRecvRet, this.currentRecvstr.length - checkRecvRet) : "";
        } else {
            if(checkRecvRet == Recv_Error_Def.ERR_START_FLAG || checkRecvRet == Recv_Error_Def.ERR_CHECKSUM){
                this.currentRecvstr = "";
            }
            return { error: checkRecvRet};
        }
        if(!this.matchAddress(message, this.address)){
            return {error: Recv_Error_Def.ERR_ADDRESS_DISMATCH};
        }
        console.log("SerialMeterClient handleReceiveMessage," + " message:" + JSON.stringify(message));

        let ret = {};
        if(this.requestList.length > 0){
            let first = this.requestList[0];
            this.requestList.shift();
            console.log("first: ", first);
            console.log("message: ", message);
            let buff = getResponseBuff(first, message);
            this.fillResultList(first, buff);
            if(this.getRequestCountByType(MeterQueryType.REALTIME) == 0){
                if(this.realtimeList.length > 0){
                    ret = {type: "message", event: "realtime", message: this.realtimeList, index: this.index, id: this.id };
                    this.realtimeList = [];
                    return ret;
                }
            }
            if(this.getRequestCountByType(MeterQueryType.STAT) == 0){
                if(this.statList.length > 0){
                    ret = {type: "message", event: "stat", message: this.statList, index: this.index, id: this.id };
                    this.statList = [];
                    return ret;
                } 
            }
        } 
        return ret;    
    }
}